home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
general
/
raytrace
/
rayshade
/
graphtal.lzh
/
Graphtal.Amiga
/
Interpreter.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-11-17
|
20KB
|
894 lines
/*
* Interpreter.C - methods for turtle interpreter.
*
* Copyright (C) 1992, Christoph Streit (streit@iam.unibe.ch)
* University of Berne, Switzerland
* All rights reserved.
*
* This software may be freely copied, modified, and redistributed
* provided that this copyright notice is preserved on all copies.
*
* You may not distribute this software, in whole or in part, as part of
* any commercial product without the express consent of the authors.
*
* There is no warranty or other guarantee of fitness of this software
* for any purpose. It is provided solely "as is".
*
*/
#include "Interpreter.h"
#include "Options.h"
#include "Polygon.h"
#include "Turtle.h"
#include "DeviceDriver.h"
#include "Module.h"
#include "Hull.h"
#include "boolean.h"
#define startBranchSymbol "["
#define endBranchSymbol "]"
#ifdef OLD_STYLE_CPP
InterpreterFunctions Interpreter::FuncTable[] = {
#else
Interpreter::InterpreterFunctions Interpreter::FuncTable[] = {
#endif
{"F", &Interpreter::forward},
{"wi", &Interpreter::width},
{"G", &Interpreter::go},
{"f", &Interpreter::go},
{"pt", &Interpreter::pitch},
{"&", &Interpreter::pitch_negativ},
{"^", &Interpreter::pitch},
{"ro", &Interpreter::roll},
{"\\", &Interpreter::roll_negativ},
{"/", &Interpreter::roll},
{"tu", &Interpreter::turn},
{"+", &Interpreter::turn_negativ},
{"-", &Interpreter::turn},
{"rv", &Interpreter::rotate_vertical},
{"$", &Interpreter::rotate_vertical},
{"|", &Interpreter::reverse},
{startBranchSymbol, &Interpreter::push},
{endBranchSymbol, &Interpreter::pop},
{"{", &Interpreter::startPolygon},
{"sv", &Interpreter::saveVertex},
{".", &Interpreter::saveVertex},
{"}", &Interpreter::endPolygon},
{"t", &Interpreter::tropism},
{"we", &Interpreter::weight},
{"co", &Interpreter::color},
{"sm", &Interpreter::beginMacro},
{"em", &Interpreter::endMacro},
{"xm", &Interpreter::executeMacro},
{"lib", &Interpreter::libraryObject},
{"s", &Interpreter::sphere},
{"tri", &Interpreter::triangle},
{"poly", &Interpreter::polygon},
{"ah", &Interpreter::activateHull},
{"dh", &Interpreter::deactivateHull},
{"cb", &Interpreter::cutBranchWhenHit},
{"%", &Interpreter::cutBranch},
{"texture", &Interpreter::texture},
{"LastFunc", NULL}
};
FP* Interpreter::functable = NULL;
Value Interpreter::turtleX;
Value Interpreter::turtleY;
Value Interpreter::turtleZ;
typedef Turtle* TurtlePtr;
declareList(TurtleStack, TurtlePtr)
implementList(TurtleStack, TurtlePtr)
//___________________________________________________________ Interpreter
Interpreter::Interpreter(DeviceDriver* device, Options* theOptions)
: d(device), options(theOptions),
t(NULL), m(NULL), ml(NULL), currentModule(-1), saveTurtle(NULL),
polyStack(new PolygonList(100)), turtleStack(new TurtleStack(100)),
definingMacro(FALSE), reflected(FALSE), deleteBranchWhenHit(FALSE)
{
/*
* Set up function table if not already done.
*/
if (functable == NULL) {
/*
* Add all the function names to global name space.
*/
for (register int i=0; FuncTable[i].function != NULL; i++)
FuncTable[i].index = Name::addname(FuncTable[i].name);
functable = new FP[Name::namesCount()];
/*
* Generate and initialize hash table for function pointers.
*/
for (i=0; i<Name::namesCount(); i++)
functable[i] = NULL;
for (i=0; FuncTable[i].function != NULL; i++)
functable[FuncTable[i].index] = FuncTable[i].function;
}
/*
* Set up analytic tropism functions and hulls.
*/
tropismX = options->tropismX;
tropismY = options->tropismY;
tropismZ = options->tropismZ;
tropismFunction =
(tropismX != NULL) || (tropismY != NULL) || (tropismZ != NULL);
tropismWeight = options->weight;
weightFunction = (tropismWeight != NULL);
h = options->hulls;
}
Interpreter::~Interpreter()
{
delete polyStack;
delete turtleStack;
}
/*
* Interpret the module string modules using the given dive driver.
*/
void Interpreter::interpret(ModuleList* modules)
{
t = new Turtle;
ml = modules;
/*
* Start interpretation of module string ml.
*/
if (!options->quiet)
cerr << "Start of Interpretation ...\n\n";
d->begin();
/*
* For each module lookup corresponding interpreter function, call it
* if not NULL.
*/
for (currentModule = 0; currentModule < ml->count(); currentModule++) {
m = ml->item(currentModule);
if (functable[ml->item(currentModule)->name().index()] != NULL) {
FP currentFunc = functable[ml->item(currentModule)->name().index()];
(this->*currentFunc)();
}
if (options->deleteModules)
delete ml->item(currentModule);
/*
* If we hit any hull primitive and deleteBranchWhenHit option is
* set, ignore all modules up to the next end branch command ("]").
* Note that we have to skip all the subbranches also.
*/
if (deleteBranchWhenHit && reflected) {
reflected = FALSE; // reset
cutBranch();
}
}
flush();
/*
* Visualize the defined hulls.
*/
if (options->showHulls) {
PolygonList* polys;
Color hullColor("HullColor");
d->color(hullColor);
for (HullSymtab_Iterator hullItr(*h); hullItr.more(); hullItr.next()) {
polys = hullItr.cur_value()->convertToPolygonList(t->bbox());
for (register long i=0; i<polys->count(); i++)
d->polygon(polys->item(i));
delete polys;
}
}
if (options->verbose) {
cerr << "BoundingBox: " << t->bbox() << "\n\n";
cerr << "intersection tests: "
<< GeoObject::getIntersectionTests() <<"\n"
<< "intersection hits: "
<< GeoObject::getIntersections() << "\n\n";
}
d->end(t->bbox());
delete t;
}
//___________________________________________________________ interpreter functions
/*
* Flush checks if there is a segment in the pipe which has't been draw yet
* (lastWidth > 0) and draws it in that case.
*/
void Interpreter::flush()
{
/*
* A segment left in the pipe?
*/
if (t->getLastWidth() > 0) {
if (tropismFunction || weightFunction)
computeTropism();
d->color(t->getLastColor());
d->texture(t->getLastTexture());
if (equal(t->getLastWidth(), t->getWidth()))
d->cylinder(t->lastPos(), t->pos(), t->getWidth());
else if (!options->coneSpheres)
d->cone(t->lastPos(), t->getLastWidth(), t->pos(), t->getWidth());
else
createConeBetweenSpheres(t->lastPos(), t->getLastWidth(),
t->pos(), t->getWidth());
if (options->coneSpheres)
d->sphere(t->pos(), t->getWidth());
}
t->setLastWidth(-1); // no segments left in the pipe
}
/*
* Go forward and draw a line.
* lastWidth < 0: no move has been previously done
*/
void Interpreter::forward()
{
real step;
/*
* Is there an argument for the move?
*/
if (m->argCount() <= 0 || !m->arg(0)->toReal(step))
step = options->defaultForward;
/*
* Do we really have to move?
*/
if (equal(step, 0)) {
Error(ERR_ADVISE, "Interpreter::forward with argument 0");
return;
}
/*
* Recompute tropism and weight functions if any present.
*/
if (tropismFunction || weightFunction)
computeTropism();
if (t->getLastWidth() > 0) {
d->color(t->getLastColor());
d->texture(t->getLastTexture());
}
real lastWidth = t->getLastWidth();
real currentWidth = t->getWidth();
Vector lastPos = t->lastPos();
Vector currentPos = t->pos();
reflected = t->forward(step);
Vector newPos = t->pos();
/*
* Regular line segment or cone spheres?
*/
if (lastWidth > 0) {
if (equal(lastWidth, currentWidth))
d->cylinder(lastPos, currentPos, lastWidth);
else if (!options->coneSpheres)
d->cone(lastPos, lastWidth, currentPos, currentWidth);
else
createConeBetweenSpheres(lastPos, lastWidth, currentPos, currentWidth);
/*
* Add sphere when there's an angle between the segments.
*/
if (options->coneSpheres && !((lastPos-currentPos)*(currentPos-newPos)).zero())
d->sphere(currentPos, currentWidth);
}
}
/*
* Go forward without drawing a line.
*/
void Interpreter::go()
{
real step;
flush();
if (tropismFunction || weightFunction)
computeTropism();
if (m->argCount()>0 && m->arg(0)->toReal(step))
reflected = t->forward(step);
else
reflected = t->forward(options->defaultForward);
/*
* There's no segment in the pipe left.
*/
t->setLastWidth(-1);
}
/*
* Set line width.
*/
void Interpreter::width()
{
real width;
if (m->argCount()>0 && m->arg(0)->toReal(width) && width>0)
t->setWidth(width);
}
/*
* Pitch turtle in positive direction.
*/
void Interpreter::pitch()
{
real a;
if (m->argCount()>0 && m->arg(0)->toReal(a))
t->pitch(dtor(a));
else
t->pitch(options->defaultPitch);
}
/*
* Pitch turtle in negative direction.
*/
void Interpreter::pitch_negativ()
{
real a;
if (m->argCount()>0 && m->arg(0)->toReal(a))
t->pitch(-dtor(a));
else
t->pitch(-options->defaultPitch);
}
/*
* Turn turtle in positive direction.
*/
void Interpreter::turn()
{
real a;
if (m->argCount()>0 && m->arg(0)->toReal(a))
t->turn(dtor(a));
else
t->turn(options->defaultTurn);
}
/*
* Turn turtle in negative direction.
*/
void Interpreter::turn_negativ()
{
real a;
if (m->argCount()>0 && m->arg(0)->toReal(a))
t->turn(-dtor(a));
else
t->turn(-options->defaultTurn);
}
/*
* Roll turtle in positive direction.
*/
void Interpreter::roll()
{
real a;
if (m->argCount()>0 && m->arg(0)->toReal(a))
t->roll(dtor(a));
else
t->roll(options->defaultRoll);
}
/*
* Roll turtle in negative direction.
*/
void Interpreter::roll_negativ()
{
real a;
if (m->argCount()>0 && m->arg(0)->toReal(a))
t->roll(-dtor(a));
else
t->roll(-options->defaultRoll);
}
/*
* Align turtle vertical.
*/
void Interpreter::rotate_vertical()
{
t->rotate_vertical();
}
/*
* Reverse heading of the turtle.
*/
void Interpreter::reverse()
{
t->reverse();
}
/*
* Save current turtle state on the stack.
*/
void Interpreter::push()
{
turtleStack->prepend(t);
t = new Turtle(*t);
t->setLastWidth(-1);
}
/*
* Restore turtle state from stack.
*/
void Interpreter::pop()
{
flush();
if (turtleStack->count() > 0) {
turtleStack->item(0)->expandBBoxBy(t->bbox());
delete t;
t = turtleStack->item(0);
turtleStack->remove(0);
}
else
Error(ERR_WARN, "Interpreter::pop stack is empty");
}
/*
* Start a new polygon.
*/
void Interpreter::startPolygon()
{
polyStack->prepend(new Polygon);
}
/*
* Add vertex to current polygon.
*/
void Interpreter::saveVertex()
{
if (polyStack->count() > 0)
polyStack->item(0)->addVertex(t->pos());
else
Error(ERR_WARN, "Interpreter::saveVertex polygon stack is empty");
}
/*
* End of polygon definition.
*/
void Interpreter::endPolygon()
{
if (polyStack->count() <= 0) {
Error(ERR_WARN, "Interpreter::endPolygon polygon stack is empty");
return;
}
Polygon* p = polyStack->item(0);
if (p->numVertices() <= 0) {
Error(ERR_WARN, "Interpreter::endPolygon no vertex in polygon");
delete p;
polyStack->remove(0);
return;
}
/*
* Start and endpoint should not be the same.
*/
if (p->numVertices()>1 && p->vertex(0) == p->vertex(p->numVertices()-1))
p->removeVertex(p->numVertices()-1);
/*
* Set color and texture.
*/
d->color(t->getColor());
d->texture(t->getTexture());
d->polygon(p);
polyStack->remove(0);
}
/*
* Set tropism vector.
*/
void Interpreter::tropism()
{
real x, y, z, w;
if (m->argCount() >= 3 &&
m->arg(0)->toReal(x) && m->arg(1)->toReal(y) && m->arg(2)->toReal(z)) {
t->setTropism(x,y,z);
if (m->argCount() >= 4 && m->arg(3)->toReal(w))
t->setWeight(w);
}
}
/*
* Set weight for tropism calculations.
*/
void Interpreter::weight()
{
real w;
if (m->argCount()>0 && m->arg(0)->toReal(w))
t->setWeight(w);
}
/*
* Set color.
*/
void Interpreter::color()
{
if (m->argCount()>0) {
Color c(m->arg(0)->toString());
t->setColor(c);
}
}
/*
* Start macro definition.
*/
void Interpreter::beginMacro()
{
if (definingMacro)
Error(ERR_PANIC, "Interpreter::beginMacro nested macro definition");
if (m->argCount() <= 0)
Error(ERR_PANIC, "Interpreter::beginMacro macro definition without a name");
rcString macroName(m->arg(0)->toString());
if (!d->addMacroName(macroName))
Error(ERR_PANIC, "Interpreter::beginMacro macro \""
+ macroName + "\" already defined");
saveTurtle = t;
t = new Turtle;
definingMacro = TRUE;
d->beginMacro(macroName);
}
/*
* End of macro definition.
*/
void Interpreter::endMacro()
{
if (definingMacro == FALSE) {
Error(ERR_WARN, "Interpreter::endMacro missing begin macro");
return;
}
flush();
d->endMacro();
delete t;
t = saveTurtle; saveTurtle = NULL;
definingMacro = FALSE;
}
/*
* Execute a already defined macro.
*/
void Interpreter::executeMacro()
{
if (m->argCount() <= 0) {
Error(ERR_WARN, "Interpreter::executeMacro macro without a name");
return;
}
TransMatrix tmat;
real scale;
tmat(0,0) = t->vecU()[0]; tmat(0,1) = t->vecU()[1]; tmat(0,2) = t->vecU()[2];
tmat(1,0) = t->vecL()[0]; tmat(1,1) = t->vecL()[1]; tmat(1,2) = t->vecL()[2];
tmat(2,0) = t->vecH()[0]; tmat(2,1) = t->vecH()[1]; tmat(2,2) = t->vecH()[2];
if (m->argCount()>1 && m->arg(1)->toReal(scale) && !equal(scale, 1))
tmat.scale(scale, scale, scale);
tmat.translate(t->pos());
d->executeMacro(m->arg(0)->toString(), tmat);
}
/*
* Include a predefined object.
*/
void Interpreter::libraryObject()
{
if (m->argCount() <= 0) {
Error(ERR_WARN, "Interpreter::libraryObject object without a name");
return;
}
TransMatrix tmat;
real scale;
rcString libName(m->arg(0)->toString());
d->addLibraryObjectName(libName);
tmat(0,0) = t->vecU()[0]; tmat(0,1) = t->vecU()[1]; tmat(0,2) = t->vecU()[2];
tmat(1,0) = t->vecL()[0]; tmat(1,1) = t->vecL()[1]; tmat(1,2) = t->vecL()[2];
tmat(2,0) = t->vecH()[0]; tmat(2,1) = t->vecH()[1]; tmat(2,2) = t->vecH()[2];
if (m->argCount()>1 && m->arg(1)->toReal(scale) && !equal(scale, 1))
tmat.scale(scale, scale, scale);
tmat.translate(t->pos());
d->libraryObject(libName, tmat);
}
/*
* Generate sphere at turtle position with given radius.
*/
void Interpreter::sphere()
{
real r;
/*
* Set color and texture.
*/
d->color(t->getColor());
d->texture(t->getTexture());
if (m->argCount()>0 && m->arg(0)->toReal(r))
d->sphere(t->pos(), r);
else
d->sphere(t->pos(), 1);
}
/*
* Generate triangle with given vertices.
*/
void Interpreter::triangle()
{
if (m->argCount() < 9)
return;
real coords[9];
for (register int i=0; i<9; i++)
if (!m->arg(i)->toReal(coords[i]))
return;
Vector p1(coords[0], coords[1], coords[2]);
Vector p2(coords[3], coords[4], coords[5]);
Vector p3(coords[6], coords[7], coords[8]);
BoundingBox tmpBBox;
tmpBBox.expand(p1);
tmpBBox.expand(p2);
tmpBBox.expand(p3);
t->expandBBoxBy(tmpBBox);
/*
* Set color and texture.
*/
d->color(t->getColor());
d->texture(t->getTexture());
d->polygon(new Polygon(p1, p2, p3));
}
/*
* Generate polygon with given vertices.
*/
void Interpreter::polygon()
{
if (m->argCount() < 9)
return;
real x, y, z;
Polygon* p = new Polygon(3);
BoundingBox tmpBBox;
for (register int i=0; i<m->argCount()-2; i+=3) {
if (m->arg(i)->toReal(x) &&
m->arg(i+1)->toReal(y) &&
m->arg(i+2)->toReal(z))
{
p->addVertex(Vector(x,y,z));
tmpBBox.expand(Vector(x,y,z));
}
}
if (p->numVertices() > 2) {
/*
* Set color and texture.
*/
d->color(t->getColor());
d->texture(t->getTexture());
d->polygon(p);
t->expandBBoxBy(tmpBBox);
}
else
delete p;
}
/*
* Activate a hull. Turtle will move with respect to the primitives
* of the hull.
*/
void Interpreter::activateHull()
{
if (m->argCount()<=0) {
Error(ERR_WARN, "Interpreter::activateHull no hull name");
return;
}
rcString hullName(m->arg(0)->toString());
Hull* hull;
if (!h->lookup(hullName, hull)) {
Error(ERR_WARN, "Interpreter::activateHull unknown hull " + hullName);
return;
}
real reflectanceFactor = 1;
if (m->argCount()>1)
m->arg(1)->toReal(reflectanceFactor);
t->setHull(hull, reflectanceFactor);
}
void Interpreter::deactivateHull()
{
t->unsetHull();
}
/*
* Set/Unset of delete branch when conflict with current hull.
*/
void Interpreter::cutBranchWhenHit()
{
real flag = TRUE;
if (m->argCount() > 0)
m->arg(0)->toReal(flag);
if ((int)flag) {
deleteBranchWhenHit = TRUE;
t->setStopOnHit();
}
else {
deleteBranchWhenHit = FALSE;
t->unsetStopOnHit();
}
}
/*
* Ignore all the modules until we find a end branch symbol (ignore
* subbranches also).
*/
void Interpreter::cutBranch()
{
static Name startBranch(startBranchSymbol);
static Name endBranch(endBranchSymbol);
int nested = 0;
/*
* Ignore all modules up to the next end branch command ("]").
* Note that we have to skip all the subbranches also.
*/
while (++currentModule < ml->count()) {
if (ml->item(currentModule)->name() == startBranch)
nested++;
else if (ml->item(currentModule)->name() == endBranch) {
if (nested-- == 0) {
currentModule--;
break;
}
}
if (options->deleteModules)
delete ml->item(currentModule);
}
}
/*
* Define texture for the following primitives.
*/
void Interpreter::texture()
{
if (m->argCount()>0) {
rcString theTexture(m->arg(0)->toString());
t->setTexture(theTexture);
}
else
t->setTexture(""); // disable textures
}
/*
* Generate a cone between two given spheres.
*/
void Interpreter::createConeBetweenSpheres(const Vector& p1, real r1,
const Vector& p2, real r2)
{
real a = r1-r2;
real r = r1;
real c = p1.distance(p2);
int secondIsLarger = FALSE;
if (c<r1 || c<r2) {
Error(ERR_ADVISE, rcString("Interpreter::createConeBetweenSpheres\n") +
"\tdistance between spheres is too small -> can't create cone\n");
return;
}
if (a < 0) {
r = r2;
a = -a;
secondIsLarger = TRUE;
}
real p = a*a/c;
real ps = p*r/a;
real h = sqrt(p*(c-p));
real hs = h*r/a;
Vector dir = p2 - p1;
if (secondIsLarger)
d->cone(p1-dir*((ps-p)/c), hs-h, p2-dir*(ps/c), hs);
else
d->cone(p1+dir*(ps/c), hs, p2+dir*((ps-p)/c), hs-h);
}
/*
* If tropism vector depends on turtle position, we recompute the
* tropism vector after each move.
*/
void Interpreter::computeTropism()
{
turtleX = t->pos()[0];
turtleY = t->pos()[1];
turtleZ = t->pos()[2];
if (tropismFunction)
t->setTropism(tropismX->evaluate(),
tropismY->evaluate(),
tropismZ->evaluate());
if (weightFunction)
t->setWeight(tropismWeight->evaluate());
}